package fst.parsing

trait Parsers extends Inputters {
  type ErrorInfo = String

	def result[T](in: Input, r: T): Success[T] = Success(r, in)(Failure("zero", in))
  def failure(in: Input): Failure = Failure("unknown failure", in)
  def error(in: Input): Error = Error("unknown error", in)
  
    
  def Parser[T](f: Input => Result[T]) 
    = new Parser[T]{ def apply(in: Input) = f(in) }

  abstract class Parser[+T] extends (Input => Result[T]) { 
    def flatMap[U](f: T => Parser[U]): Parser[U] 
      = Parser{in => this(in) flatMapWithInfo(f)}

    def map[U](f: T => U): Parser[U] 
      = Parser{in => this(in) map(f)}

    def filter(f: T => Boolean): Parser[T]
      = Parser{in => this(in) filter(f)}
    
    def | [U >: T](p: => Parser[U]): Parser[U] 
      = Parser{in => Parser.this(in) append p(in)}
  
    def ~ [U](p: => Parser[U]): Parser[Pair[T, U]] 
      = for(a <- this; b <- p) yield (a,b)
      // flatMap{a => p.map{b => (a, b)}}
      
    def ~> [U](p: => Parser[U]): Parser[U] = for(a <- this; b <- p) yield b
    def <~ [U](p: => Parser[U]): Parser[T] = for(a <- this; b <- p) yield a

    def >> [U](f: T => Parser[U]): Parser[U] = flatMap(f)
    def ^^ [U](f: T => U): Parser[U] = map(f)
      
    def explainWith(msg: Input => String): Parser[T] = Parser{in =>
      this(in) explain msg(in)
    }
  
    def explain(msg: String): Parser[T] = Parser{in =>
      Parser.this(in) explain msg
    }

    def expected(kind: String): Parser[T] 
      = explainWith{in => ""+ kind +" expected, but \'"+ in.first +"\' found."}

/*    def dontBacktrack: Parser[T] = Parser{in => 
      this(in) match {
        case s@Success(x, n) => Success(x, n)(s.zero.toError)
        case ns : NoSuccess => ns.toError
      }
    }

    def ! = dontBacktrack
    
    def ~! [U](p: => Parser[U]): Parser[Pair[T, U]] 
      = dontBacktrack ~ p*/  
  }  

  def consumeFirst: Parser[Elem] = Parser{in => 
    Success(in.first, in.rest)(in.failure)
  }
  
  def acceptIf(p: Elem => Boolean): Parser[Elem] = consumeFirst filter(p)
  // = for(e <- consumeFirst; if p(e)) yield e
  implicit def accept(e: Elem): Parser[Elem] = acceptIf(_ == e).expected(e.toString)  

  def log[T](p: => Parser[T])(name: String): Parser[T] = Parser{in => 
    println("trying "+ name +" at \'"+ in + "\'")
    val r = p(in)
    println(name +" --> "+ r)
    r
  }
  
  
// for convenience :

  // use with ^^ to lift to a constant  
  def const[U, T](c: T): U => T = {x: U => c}
  
  // to make pattern matching on nested pairs easier
  // `case ((a, b), c) => ... ` becomes `case a ~ b ~ c => ... `            
  object ~ {
    def unapply[T, U](p: (T, U)): Some[(T, U)] = Some(p)
  }
  //val ~ = Pair // --> bug!
}

trait MoreCombinators extends Parsers {
  def success[T](v: T): Parser[T] = Parser{in => Success(v, in)(in.failure)}
  def failure: Parser[Nothing] = Parser{in => in.failure}
  def error: Parser[Nothing] = Parser{in => in.error}
    
  def opt[T](p: => Parser[T]): Parser[Option[T]] 
    = ( p ^^ {x: T => Some(x)} 
      | success(None)
      )

  def rep[T](p: => Parser[T]): Parser[List[T]] 
    = rep1(p) | success(List())

  def rep1[T](p: => Parser[T]): Parser[List[T]] 
    = rep1(p, p)
  
  def rep1[T](first: => Parser[T], p: => Parser[T]): Parser[List[T]] 
    = first ~ rep(p) ^^ mkList

  def repsep[T, S](p: => Parser[T], q: => Parser[S]): Parser[List[T]]  
    = rep1sep(p, q) | success(List())
    
  def rep1sep[T, S](p: => Parser[T], q: => Parser[S]): Parser[List[T]]
    = rep1sep(p, p, q)
    
  def rep1sep[T, S](first: => Parser[T], p: => Parser[T], q: => Parser[S]): Parser[List[T]] 
    = first ~ rep(q ~> p) ^^ mkList
    
  def chainl1[T](p: => Parser[T], q: => Parser[(T, T) => T]): Parser[T] 
    = chainl1(p, p, q)
    
  def chainl1[T, U](first: => Parser[T], p: => Parser[U], q: => Parser[(T, U) => T]): Parser[T] 
    = first ~ rep(q ~ p) ^^ {
        case (x, xs) => xs.foldLeft(x){(_, _) match {case (a, (f, b)) => f(a, b)}}
      }

  def acceptSeq[ES <% Iterable[Elem]](es: ES): Parser[List[Elem]] 
    = es.foldRight[Parser[List[Elem]]](success(Nil)){(x, pxs) => (accept(x) ~ pxs) ^^ mkList}
  
  private def mkList[T] = (_ : Pair[T, List[T]]) match {case (x, xs) => x :: xs }
}
